package com.chinanet.service.faultanticipate.controll;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.chinanet.entry.faultanticipate.Detail;
import com.chinanet.entry.faultanticipate.Model;
import com.chinanet.entry.faultanticipate.Operation;
import com.chinanet.entry.faultanticipate.ResultLog;
import com.chinanet.event.entry.RecordExecuteEvent;
import com.chinanet.message.entry.faultanticipate.OperationMessage;
import com.chinanet.repository.faultanticipate.*;
import com.chinanet.service.faultanticipate.LoadModel;
import com.chinanet.service.faultanticipate.flowmodel.Branch;
import com.chinanet.service.faultanticipate.flowmodel.FlowModel;
import com.chinanet.service.faultanticipate.flowmodel.Line;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;

/**
 * Created by moon on 16/9/27.
 */
@Service
public class ModelService {
    @Autowired
    private SimpMessagingTemplate template;

    @Autowired
    private ApplicationEventPublisher publisher;

    @Autowired
    private DetailRepository detailRepository;

    @Autowired
    private ModelRepository modelRepository;

    @Autowired
    private OperationRepository operationRepository;

    @Autowired
    private RecordRepository recordRepository;

    @Autowired
    private LoadModel loadModel;

    @Autowired
    private LogRepository logRepository;

    public static final String REPLY = "flag";

    public static final int SUCCESS = 1;

    public static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    //外部执行模型
    public void runModelOrigin(Operation operation, String id, Map<String, Object> datas, String auth) {
        Model model = modelRepository.findById(id).get();
        if (model != null) {
            System.out.println("模型开始执行");
            runModel(operation,model.getContent(), model.getBranch(), datas, model.getSelect(), auth, id);
        }
    }

    //执行模型
    private boolean runModel(Operation operation, String jsonstr, String branchstr, Map<String, Object> origindata, String selectin, String auth, String modelid) {
        //初始化模型
        FlowModel model = new FlowModel(jsonstr);
        Branch branch = new Branch(branchstr);
        ArrayList<Line> list = model.findTobyFrom("tz_node_begin");
        Map<String, Object> data = new HashMap<String, Object>();
        int id = IdStrategy.getId();
        //将初始入参加入参数池
        for (Map.Entry<String, Object> entry : origindata.entrySet()) {
            System.out.println(entry.getKey() + "-Origin||" + entry.getValue());
            data.put(entry.getKey() + "-Origin", entry.getValue());
        }
        int index = 0;
        try {
            outside:
            while (list.size() > 0) {
                //默认条件
                if (list.size() == 1) {
                    Map<String, Object> tp = doNodeAction(operation,list.get(0).getTo(), data, selectin, auth, id, modelid, index);
                    index++;
                    if (Integer.parseInt(tp.get(REPLY).toString()) == SUCCESS) {
                        data.putAll(tp);//加入参数池
                        list = model.findTobyFrom(list.get(0).getTo());
                    } else {
                        error(operation,tp, auth, id, modelid, index);
                        index++;
                        return false;
                    }
                } else {
                    //非默认条件
                    for (int i = 0; i < list.size(); i++) {
                        if (branch.isSatisfy(list.get(i).getName(), data)) {
                            Map<String, Object> tp = doNodeAction(operation,list.get(i).getTo(), data, selectin, auth, id, modelid, index);
                            index++;
                            if (Integer.parseInt(tp.get(REPLY).toString()) == SUCCESS) {
                                list = model.findTobyFrom(list.get(i).getTo());
                                data.putAll(tp);//加入参数池
                            } else {
                                error(operation,tp, auth, id, modelid, index);
                                index++;
                                return false;
                            }
                            continue outside;
                        }
                    }
                    for (int i = 0; i < list.size(); i++) {
                        //System.out.println(list.get(i).getName()+"|"+i);
                        if (branch.isDefault(list.get(i).getName())) {
                            Map<String, Object> tp = doNodeAction(operation,list.get(i).getTo(), data, selectin, auth, id, modelid, index);
                            index++;
                            if (Integer.parseInt(tp.get(REPLY).toString()) == SUCCESS) {
                                data.putAll(tp);//加入参数池
                                list = model.findTobyFrom(list.get(i).getTo());
                            } else {
                                error(operation,tp, auth, id, modelid, index);
                                index++;
                                return false;
                            }
                            continue outside;
                        }
                    }
                    error(operation,data, auth, id, modelid, index);
                    index++;
                    return false;
                }
            }
        } catch (Exception e) {
            Map<String, Object> info = new HashMap<>();
            info.put("log", e.toString() + e.getMessage());
            error(operation,info, data, "", auth, id, modelid, index);
            return false;
        }
        return true;
    }

    //执行网元能力
    private Map<String, Object> doNodeAction(Operation operation,String nodeid, Map<String, Object> data, String selectin, String auth, int id, String modelid, int index) {
        if (nodeid.equals("tz_node_end")) {
            System.out.println("模型执行结束");
            Map<String, Object> reply = new HashMap<String, Object>();
            reply.put(REPLY, SUCCESS);
            log(operation,"模型执行结束", null, nodeid, auth, data, id, modelid, index);
            return reply;
        } else if (nodeid.equals("tz_node_error")) {
            System.out.println("");
            Map<String, Object> reply = new HashMap<String, Object>();
            reply.put(REPLY, SUCCESS);
            log(operation,"模型执行失败", null, nodeid, auth, data, id, modelid, index);
            return reply;
        } else {
            Map<String, Object> reply = new HashMap<String, Object>();
            Map<String, Object> temp;
            reply.put(REPLY, SUCCESS);
            temp = loadModel.entranceFactory(
                    nodeid.split("-")[0],//取得节点名称
                    getInData(nodeid, data, selectin)//清除入参后缀
            );
            //记录日志
            Object x = temp.get("show");
            if (x != null) {
                log(operation,temp.get("log").toString(), x.toString() + "<br>", nodeid, auth, data, id, modelid, index);
            } else {
                log(operation,temp.get("log").toString(), null, nodeid, auth, data, id, modelid, index);
            }
            //为出参添加后缀
            String last = nodeid.split("-")[1];
            for (String key : temp.keySet()) {
                reply.put(key + "-" + last, temp.get(key));
            }
            reply.remove("log");
            return reply;
        }
    }

    //匹配入参 - 清除后缀
    private Map<String, Object> getInData(String nodeid, Map<String, Object> data, String selectin) {
        Map<String, Object> map = new HashMap<>();
        if (selectin.length() > 2) {
            String tp = selectin.substring(2, selectin.length() - 2);
            String list[] = tp.split("\",\"");
            for (String x : list) {
                String ts[] = x.split("&");
                if (ts.length == 3) {
                    if (ts[0].equals(nodeid)) {
                        map.put(ts[1], data.get(ts[2]));
                    }
                }
            }
        }
        return map;
    }


    private void log(Operation operation,String log, String show, String nodeid, String auth, Map<String, Object> data, int id, String modelid, int index) {
        /**
         * 这里记录日志
         */
        ResultLog errorlog = new ResultLog();
        errorlog.setpId("MODEL_LOG.nextval");
        errorlog.setIns(JSON.toJSONString(data));
        errorlog.setRecordId(recordRepository.findByModelId(modelid).getId());
        errorlog.setOperationId(operation.getId());
        errorlog.setInId(id);
        errorlog.setLogInfo(log);
        errorlog.setShowResult(show);
        errorlog.setOdbs(index);
        errorlog.setNode(nodeid);
        logRepository.save(errorlog);

        operation.setStatus(2);
        operation.setOutput(JSON.toJSONString(data));
        operationRepository.save(operation);
        System.out.println("节点{" + nodeid + "}日志记录:" + log);
    }

    //网元能力执行过程出错，记录
    private void error(Operation operation,Map<String, Object> info, Map<String, Object> in, String node, String auth, int id, String modelid, int index) {
        ResultLog errorlog = new ResultLog();
        errorlog.setpId("MODEL_LOG.nextval");
        errorlog.setIns(JSON.toJSONString(in));
        errorlog.setRecordId(recordRepository.findByModelId(modelid).getId());
        errorlog.setOperationId(operation.getId());
        errorlog.setInId(id);
        errorlog.setLogInfo(JSON.toJSONString(info));
        errorlog.setShowResult("执行过程报错");
        errorlog.setOdbs(index);
        errorlog.setNode(node);
        errorlog.setType(1);
        logRepository.save(errorlog);

        operation.setStatus(3);
        operation.setOutput("执行过程报错");
        operationRepository.save(operation);
        System.out.println("报错啦");
    }

    //网元能力执行过程出错，记录
    private void error(Operation operation,Map<String, Object> in, String auth, int id, String modelid, int index) {

        ResultLog errorlog = new ResultLog();
        errorlog.setpId("MODEL_LOG.nextval");
        errorlog.setIns(JSON.toJSONString(in));
        errorlog.setRecordId(recordRepository.findByModelId(modelid).getId());
        errorlog.setOperationId(operation.getId());
        errorlog.setInId(id);
        errorlog.setLogInfo("无符合条件");
        errorlog.setShowResult("无符合条件");
        errorlog.setOdbs(index);
        errorlog.setNode("无节点");
        errorlog.setType(1);
        logRepository.save(errorlog);
        operation.setStatus(3);
        operation.setOutput("条件错误");
        operationRepository.save(operation);
        System.out.println("条件错误");
    }
    @Async
    @Transactional
    public Future<Long> execute(String model, String data, Detail detail, Operation operation) {
        System.out.println(data);
        JSONObject root = JSON.parseObject(data);
        JSONObject obj=JSON.parseObject(root.getString("data"));
        Map<String, Object> map = new HashMap<>();
        for (Map.Entry<String, Object> entry : obj.entrySet()) {
            System.out.println(entry.getKey() + "|" + entry.getValue());
            map.put(entry.getKey(), entry.getValue());
        }
        String username = null;
        operation.setInput(root.getString("data"));
        try{
            this.runModelOrigin(operation,model, map, username == null ? "unknow" : username);
            operation.setOutput("执行完成");
            operation.setStatus(2);
            operationRepository.save(operation);
        }catch (Exception e){
            e.printStackTrace();
            return new AsyncResult<Long>(operation.getId());
        }

        System.out.println("模型执行结束");
        // 无论好坏都是存储数据成功operationId;
        publisher.publishEvent(new RecordExecuteEvent(detail.getId()));
        // 向前端发送操作完成通知
        this.sendResult("/topic/model",operation.getId());
        return new AsyncResult<Long>(operation.getId());
    }
    public void sendResult(String destination,long id){
        System.out.println("发送消息");
        this.template.convertAndSend(destination,new OperationMessage(id));
    }
}
